using System;
using System.ComponentModel;
using System.Threading;
using GeneralDFS.Common;
using GeneralDFS.BusinessLogical.Nodes;
using GeneralDFS.DataEntity.Nodes;

namespace GeneralDFS.BusinessLogical.RemoteObject
{
    public partial class NameNodeRemoteObject
    {
        /// <summary>
        /// Maintenance task to ensure that the local node has valid successor node.  Roughly equivalent
        /// to what is called out in the Chord paper.
        /// </summary>
        /// <param name="sender">The worker thread the task is running on.</param>
        /// <param name="ea">Args (ignored here).</param>
        private void StabilizeSuccessors(object sender, DoWorkEventArgs ea)
        {
            var me = (BackgroundWorker)sender;
            while (!me.CancellationPending)
            {
                try
                {
                    // check in successor and if it's bad, replace it with
                    // the next live entry in the successor cache
                    var succPredNode = NameNode.GetPredecessor(this.Successor);
                    if (succPredNode != null)
                    {
                        if (DFSUtils.IsIDInRange(succPredNode.ID, this.ID, this.Successor.ID))
                        {
                            this.Successor = succPredNode;
                        }

                        // ignoring return because bad node will be detected on next invocation
                        NameNode.CallNotify(this.Successor, _nameNode.LocalNode);
                        GetSuccessorCache(this.Successor);
                    }
                    else
                    {
                        bool successorCacheHelped = false;
                        foreach (var entry in this._successorCache)
                        {
                            var instance = NameNode.GetInstance(entry);
                            if (NameNode.IsInstanceValid(instance))
                            {
                                this.Successor = entry;
                                NameNode.CallNotify(this.Successor, _nameNode.LocalNode);
                                GetSuccessorCache(this.Successor);
                                successorCacheHelped = true;
                                break;
                            }
                        }

                        // if we get here, then we got no help and have no other recourse than to re-join using the initial seed...
                        if (!successorCacheHelped)
                        {
                            Join(this._seedNode);
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    LogUtil.LogInfo("Error occured during StabilizeSuccessors ({0})", e.Message);
                }

                // TODO: this could be tweaked and/or made configurable elsewhere or passed in as arguments
                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// Get the successor cache from a remote node and assign an altered version the local successorCache.
        /// Gets the remote successor cache, prepends remoteNode and lops off the last entry from the remote
        /// successorcache.
        /// </summary>
        /// <param name="remoteNode">The remote node to get the succesorCache from.</param>
        private void GetSuccessorCache(NameNodeInfo remoteNode)
        {
            NameNodeInfo[] remoteSuccessorCache = NameNode.GetSuccessorCache(remoteNode);
            if (remoteSuccessorCache != null)
            {
                this.SuccessorCache[0] = remoteNode;
                for (int i = 1; i < this.SuccessorCache.Length; i++)
                {
                    this.SuccessorCache[i] = remoteSuccessorCache[i - 1];
                }
            }
        }
    }
}
